package testApplets;

import java.awt.Container;
import java.awt.Frame;
import java.awt.Menu;
import java.awt.MenuBar;
import java.awt.MenuItem;
import java.awt.event.ActionEvent;
import java.awt.event.ActionListener;
import java.awt.event.ItemListener;
import java.io.File;
import java.util.ArrayList;
import java.util.Arrays;
import java.util.Vector;

import marchingCubes.CAADMCube;

import blobDetector.Blob;
import blobDetector.BlobDetector;

import processing.core.PApplet;
import processing.core.PImage;
import simpleGeom.Box3D;
import simpleGeom.DepthAnalyser2D;
import simpleGeom.DepthAnalyser3D;
import simpleGeom.FloatGrid3D;
import simpleGeom.Rect2D;
import simpleGui.Gui;
import simpleGui.GuiClickButton;
import simpleGui.GuiSpinnerGroup;
import simpleGui.PCam2DGraphics;
import testApplets.StreamMeshDistance.Boundary;

public class CopyOfFinalCutBrowserBackup extends PApplet {
	Gui gui;
	GuiSpinnerGroup spinnerFrom;
	GuiSpinnerGroup spinnerTo;
	PImage imgCanvas;
	PImage imgNew;
	PImage imgOffset;
	boolean[]solids;
	boolean[]repairGrid;
	int nX=1600;
	int nY;
	int nZ=1620;
	String pathCuts="/Users/caad/Desktop/Grotto - First Half V/data/cuts";
	String pathFixed="/Users/caad/Desktop/Grotto - First Half V/data/fixed";
	String pathBoxes="/Users/caad/Desktop/Grotto - First Half V/data/boxes.txt";
	String pathOffset="/Users/caad/Desktop/Grotto - First Half V/data/offset";
	float scale;
	PCam2DGraphics cam;
	final static int SOLID=0;
	final static int PARTS=1;
	int mode=SOLID;
	
	
	int bdX1= 50+1;
	int bdX2= 1549-1;
	int bdY1= 55;
	int bdY2= 3269;
	int bdZ1= 49+1;
	int bdZ2= 1589-1;
	Box3D bdsFloat;//this stays for ever

	Boundary[][] boundaries;
	
	
	GuiSpinnerGroup spX1;
	GuiSpinnerGroup spX2;
	GuiSpinnerGroup spY1;
	GuiSpinnerGroup spY2;
	GuiSpinnerGroup spZ1;
	GuiSpinnerGroup spZ2;
	GuiSpinnerGroup selectSpinner;
	GuiClickButton guiAddBox;
	
	int cY=0;


	Box3D currentBox;
	Box3D selectedBox;
	ArrayList<Box3D> fillBoxes=new ArrayList<Box3D>(); 

	PImage img;


	boolean flagSaveBoxes=false;
	boolean flagAddBox=false;
	boolean flagDeleteBox=false;
	int[] coords;
	Box3D offsetBox;
	
	int[] blobs;
	int[] candidates;
	int[] newCandidates;
	ArrayList<Box3D>blobBoxes=new ArrayList<Box3D>();
	ArrayList<Box3D>offsetBoxes=new ArrayList<Box3D>();
	ArrayList<Box3D>offsetBoxes2=new ArrayList<Box3D>();
	ArrayList<Box3D>solidBoxes= new ArrayList<Box3D>();
	ArrayList<Box3D>solidBlobBoxes= new ArrayList<Box3D>();
	ArrayList<Box3D>solidBlobBoxes3= new ArrayList<Box3D>();
	ArrayList<Box3D>blobBoxes2=new ArrayList<Box3D>();
	ArrayList<Box3D>blobBoxes3=new ArrayList<Box3D>();
	ArrayList<Box3D>allBoxes=new ArrayList<Box3D>();
	
	boolean displayBoxes;
	int[] yCuts=new int[]{56,456,858,1661,2464,2865,3267};
	int[] boxX;
	int[] boxY;
	int[] boxZ;
	int cyan=color(255,255,0);
	int[][] blobInfo;
	float sX;
	float sY;
	float sZ;
	float eX;
	float eY;
	float eZ;
	String path = "/Users/caad/Desktop/Grotto - First Half V/03_scaled/";
	String voxelPath="/Users/caad/Desktop/Grotto - First Half V/voxel_distance_1mm/";
	VoxelStream vStream;
	float dim=0.001f;
	float iso = dim *0.9f;
	//Box3D cB=new Box3D(0.00f,0.000f,-1.52f,1.5f,3.25f,0.00f);//this stays for ever
	Box3D gridBounds;
	float[] xSegs;
	float[] zSegs;
	int step=1;
	public void mirrorXGrid(float[][][]grid,Box3D b3d,int limit){
		
		int mX=(int)b3d.x2;
		for (int x=(int)b3d.x1;x<b3d.x2;x++){
			for (int y=(int)b3d.y1;y<b3d.y2;y++){
				for (int z=(int)b3d.z1;z<b3d.z2;z++){
					
					if (grid[x][y][z]>-iso){
						float mirrorX=(mX-x)+mX;
						if (mirrorX<limit){
						grid[(mX-x)+mX][y][z]=grid[x][y][z];
						}
					}
				}
			}
		}
	}
	public float[][][] mirrorYGrid(float[][][]grid,Box3D b3d,float my,int limit){
//		float[][][] mirrorGrid=new float[][][];
//		
//		
//		for (int x=(int)b3d.x1;x<b3d.x2;x++){
//			for (int y=(int)b3d.y1;y<b3d.y2;y++){
//				for (int z=(int)b3d.z1;z<b3d.z2;z++){
//					
//					if (grid[x][y][z]>-iso){
//						float mY=(mX-x)+mX;
//						if (mirrorX<limit){
//						grid[(mX-x)+mX][y][z]=grid[x][y][z];
//						}
//					}
//				}
//			}
//		}
		return null;
	}
	public void makeSolid(int y1,int y2){
		int offset=20;
		for (int y=y1;y<y2;y++){
			println("y: "+y);
			loadCuts(y,solids);
			imgOffset=this.loadImage(getPathOffset(y));
			imgOffset.loadPixels();
			
			blobInfo= blobs();
			for (int i=0;i<offsetBoxes.size();i++){
				Box3D b=offsetBoxes.get(i);
				if (b.y1<=y&&b.y2>=y){
					offsetthinLines(b,offset);
				}
			}
			
			for (int i=0;i<offsetBoxes2.size();i++){
				Box3D b=offsetBoxes2.get(i);
				if (b.y1<=y&&b.y2>=y){
					offsetthinLines2(b,offset);
				}
			}
			for (int i=0;i<blobBoxes.size();i++){
				Box3D b=blobBoxes.get(i);
				if (b.y1<=y&&b.y2>=y){
					fillBobs3D(b);
				}
			}
			for (int i=0;i<solidBoxes.size();i++){
				Box3D b=solidBoxes.get(i);
				if (b.y1<=y&&b.y2>=y){
					fillSolid(b);
				}
			}
			
			
			for (int i=0;i<solidBlobBoxes.size();i++){
				Box3D b=solidBlobBoxes.get(i);
				if (b.y1<=y&&b.y2>=y){
					fillSolidArray(b);
				}
			}
			blobInfo= blobs();
			for (int i=0;i<blobBoxes2.size();i++){
				Box3D b=blobBoxes2.get(i);
				if (b.y1<=y&&b.y2>=y){
					fillBobs3D(b);
				}
			}
			
			loadCuts(y,solids);
			for (int i=0;i<solidBlobBoxes3.size();i++){
				Box3D b=solidBlobBoxes3.get(i);
				if (b.y1<=y&&b.y2>=y){
					fillSolidArray(b);
				}
			}
			blobInfo= blobs();
			for (int i=0;i<blobBoxes3.size();i++){
				Box3D b=blobBoxes3.get(i);
				if (b.y1<=y&&b.y2>=y){
					fillBobs3D(b);
				}
			}
			imgOffset.updatePixels();
			imgOffset.save(getPathOffset(y));
		}
	}
	
	public void setBounds(){
		Box3D cB=new Box3D(0.00f,0.000f,-1.52f,1.5f,3.25f,0.00f);//this stays for ever
		Box3D gridBounds=cB.getOffsetBox(0.05f);
		int  y1=getRoundDown(gridBounds.y1,dim);
		int  x1=getRoundDown(gridBounds.x1,dim);
		int  z1=getRoundDown(gridBounds.z1,dim);
		
		int  y2=getRoundUp(gridBounds.y2,dim);
		int  x2=getRoundUp(gridBounds.x2,dim);
		int  z2=getRoundUp(gridBounds.z2,dim);
		
		nX=x2-x1;
		nY=y2-y1;
		nZ=z2-z1;
		
		sX=x1*dim;
		sY=y1*dim;
		sZ=z1*dim;
		eX=x2*dim;
		eY=y2*dim;
		eZ=z2*dim;
		
		float bdy1=cB.y1+0.005f;
		
		
		float bdx1=cB.x1;
		float bdz1=cB.z1+0.001f;
		float bdy2=cB.y2-0.030f;
		float bdx2=cB.x2;
		float bdz2=cB.z2+0.020f-0.006f;
		this.bdsFloat=new Box3D(bdx1,bdy1,bdz1,bdx2,bdy2,bdz2);
		
		this.bdX1=xK(bdx1);
		this.bdX2=xK(bdx2);
		this.bdY1=yK(bdy1);
		this.bdY2=yK(bdy2);
		this.bdZ1=zK(bdz1);
		this.bdZ2=zK(bdz2);
		
		float dX=(bdx2-bdx1)/6f;
		xSegs=new float[7];
		for (int i=0;i<xSegs.length;i++){
			xSegs[i]=bdx1+i*dX;
			
		}
		zSegs=new float[7];
		float dZ=(bdz2-bdz1)/6f;
		for (int i=0;i<zSegs.length;i++){
			zSegs[i]=bdz1+i*dZ;
		}
		
		
		int z0=1569;
		boxY=new int[9];
		for (int i=0;i<8;i++){
			boxY[i]=yK((i*(bdy2-bdy1)/8f)+bdy1);
		}
		boxY[8]=yK(bdy2);
		
		
		boxZ=new int[7];
		for (int i=0;i<boxZ.length;i++){
			boxZ[i]=zK(zSegs[i]);
		}
		boxZ[boxZ.length-1]=bdZ2;
		
		boxX=new int[7];
		for (int i=0;i<boxX.length;i++){
			boxX[i]=xK(xSegs[i]);
		}
		boxX[boxX.length-1]=bdX2;
		
		boundaries=new Boundary[6][4];
		for (int i=0;i<6;i++){
			boundaries[i][0]=new Boundary(boxX[0],boxZ[0],boxX[5],boxZ[1]);
			boundaries[i][1]=new Boundary(boxX[5],boxZ[0],boxX[6],boxZ[4]);
			boundaries[i][2]=new Boundary(boxX[5],boxZ[4],boxX[6],boxZ[6]);
			boundaries[i][3]=new Boundary(boxX[0],boxZ[1],boxX[5],boxZ[6]);
		}
		
		boundaries[0]=new Boundary[5];
		boundaries[0][0]=new Boundary(boxX[3],boxZ[1],boxX[5],boxZ[6]);
		boundaries[0][1]=new Boundary(boxX[0],boxZ[1],boxX[3],boxZ[6]);
		boundaries[0][2]=new Boundary(boxX[0],boxZ[0],boxX[5],boxZ[1]);
		boundaries[0][3]=new Boundary(boxX[5],boxZ[0],boxX[6],boxZ[4]);
		boundaries[0][4]=new Boundary(boxX[5],boxZ[4],boxX[6],boxZ[6]);
	}
	
	public void setup() {
		size(1400, 800);
		initOffsetImages();
		setupMenu();
		setupGUI();
		setBounds();
		vStream=new VoxelStream(voxelPath, dim, sX, sY, sZ, nX, nY, nZ);
		solids=new boolean[nX*nZ];
		repairGrid=new boolean[nX*nZ];
		blobs=new int[solids.length];
		candidates=new int[solids.length];
		newCandidates=new int[solids.length];
		imgCanvas=this.createImage(nX, nZ, ARGB);
		imgNew=this.createImage(nX, nZ, ARGB);
		
		//initBoundaries();
		
		offsetBoxes.add(new Box3D(56,bdY1+2,bdZ1,1000,1660,500));
		offsetBox=offsetBoxes.get(0);
		//println("bdx1: "+bdX1);
		offsetBoxes2.add(new Box3D(bdX1,2609,bdZ1,406,2955,460));
		//offsetBox=;
		blobBoxes.add(new Box3D(56,bdY1+2,bdZ1,1000,bdY1+550,500));
		blobBoxes.add(new Box3D(1200,bdY1+2,630,1550,2400,1580));// hinten links
		blobBoxes.add(new Box3D(740,bdY1+2,170,1390,2550,830));// mitte
		blobBoxes.add(new Box3D(56,242,170,600,678,530));// vorne oben
		blobBoxes.add(new Box3D(bdX1+1,400,070,485,678,540));// vorne oben2
		blobBoxes.add(new Box3D(200,1199,056,400,1669,420));// vorne oben2
		blobBoxes.add(new Box3D(292,2516,130,667,2836,551));//  oben
		blobBoxes.add(new Box3D(120,679,160,371,1200,428));//  oben
		blobBoxes.add(new Box3D(bdX1+1,2566,56,265,3000,410));//  oben
		blobBoxes.add(new Box3D(160,1923,bdZ1-1,399,2199,240));//  oben
		blobBoxes.add(new Box3D(400,2246,bdZ1-1,551,2471,250));//  oben
		
		blobBoxes.add(new Box3D(80,1300,56,255,1655,190));//  oben
		blobBoxes.add(new Box3D(200,2744,66,500,2945,420));//  oben
		blobBoxes.add(new Box3D(200,2756,bdZ1+1,360,2957,220));//  oben
		blobBoxes.add(new Box3D(bdX1+1,2675,280,240,2711,490));//  oben
		blobBoxes.add(new Box3D(756,2600,800,1500,3268,bdZ2-2));//  oben
		
		solidBoxes.add(new Box3D(1055,bdY1+2,boxZ[2],1550,bdY1+550,1070));
		solidBoxes.add(new Box3D(boxX[4],2500,boxZ[2],1550,bdY2,boxZ[4]));
		allBoxes.addAll(blobBoxes);
		allBoxes.addAll(solidBoxes);
		allBoxes.addAll(offsetBoxes);
		allBoxes.addAll(offsetBoxes2);
		//rebuildImages(400,678);
		
		Box3D bFillblob=new Box3D(980,800,boxZ[2],bdX2,2550,bdZ2);
		solidBlobBoxes.add(bFillblob);
		Box3D bInside=new Box3D(800,800,450,bdX2,2550,bdZ2);
		blobBoxes2.add(bInside);
		
		solidBlobBoxes3.add(new Box3D(bdX1,820,bdZ1,bdX2,1980,boxZ[2]));
		blobBoxes3.add(new Box3D(1170,820,925,1500,1980,1328));
		
		//makeSolid(2500,3268);
		//this.loadBoxes(pathBoxes);
		int iLayer=1;
		
		step=1;
		for (int i=0;i<1;i++){
			iLayer=i;
			this.saveObjLevelFast(yCuts[iLayer], yCuts[iLayer+1], boundaries[i],path+"1mm_test5_"+iLayer);
		}
		//this.saveObjLevel(yCuts[4], yCuts[5], boundaries);
		loadCuts(56,solids);
		writeToImage(solids,imgCanvas);
		
		
	}
	// z1=497
	public void initOffsetImages(){
		for (int y=bdY1;y<bdY2;y++){
			//
			String offsetPath=getPathOffset(y);
			File file=new File(offsetPath);
			if (!file.exists()){
				println("image y recreated: "+y);
				PImage img=this.createImage(nX,nZ,ARGB);
				img.save(offsetPath);
			}
			
		}
	}
	public void rebuildImages(int y1,int y2){
		for (int y=y1;y<y2;y++){
			println("y: "+y);
			String offsetPath=getPathOffset(y);
			File file=new File(offsetPath);
			
				PImage img=this.createImage(nX,nZ,ARGB);
				img.save(offsetPath);
			
			
		}
	}
	public void offsetthinLines(Box3D box,float offset,int x,int y){
		int[] coords=new int[]{x,y};
		offsetthinLines(box,offset,coords);
	}
	
	public void fillBobs3D(Box3D box) {
		
		for (int i = 0; i < blobInfo.length; i++) {
			int[] blob = blobInfo[i];
			if (blob[0] > box.x1 && blob[2] < box.x2 && blob[1] > box.z1
					&& blob[3] < box.z2) {
				for (int j = 0; j < blobs.length; j++) {
					if (blobs[j] == i) {
						if (solids[j] == true)
							break;
						imgOffset.pixels[j] = cyan;
					}
				}
			}
		}
	}
	public void fillSolidArray(Box3D box) {
		for (int x=(int)box.x1;x<box.x2;x++){
			for (int y=(int)box.z1;y<box.z2;y++){
				int index=getIndex(x,y);
				solids[index]=true;
			}
		}		
	}
	public void fillSolid(Box3D box) {
		for (int x=(int)box.x1;x<box.x2;x++){
			for (int y=(int)box.z1;y<box.z2;y++){
				int index=getIndex(x,y);
				if (!solids[index]){
					imgOffset.pixels[index] = cyan;
				}
			}
		}		
	}
	public void offsetthinLines(Box3D box,float offset){
		int cI=0;
		coords=new int[(int)(offsetBox.z2-offsetBox.z1-1)*2];
		for (int z=(int)offsetBox.z1+1;z<offsetBox.z2;z++){
			coords[cI]=(int)offsetBox.x1+1;
			coords[cI+1]=(int)z;
			cI+=2;
		}
		offsetthinLines( box, offset,coords);
	}
	public void offsetthinLines2(Box3D box,float offset){
		int cI=0;
		coords=new int[(int)(offsetBox.z2-offsetBox.z1-1)*2];
		for (int z=(int)offsetBox.z1+1;z<offsetBox.z2;z++){
			coords[cI]=(int)offsetBox.x1+1;
			coords[cI+1]=(int)z;
			cI+=2;
		}
		offsetthinLines2( box, offset,coords);
	}

	public void offsetthinLines2(Box3D box, float offset, int[] coords) {
		int x1 = (int) box.x1;
		int x2 = (int) box.x2;
		int y1 = (int) box.z1;
		int y2 = (int) box.z2;
		float[] offests = new float[solids.length];
		Arrays.fill(offests, 10000);
		
		for (int i = 0; i < blobInfo.length; i++) {
			int[] blob = blobInfo[i];
			if (blob[0] >= box.x1 && blob[2] <= box.x2 && blob[1] >= box.z1
					&& blob[3] <= box.z2) {
				boolean contains=false;
				for (int j=0;j<coords.length;j+=2){
					int index=this.getIndex(coords[j], coords[j+1]);
					if (blobs[index]==i){
						contains=true;
					}
				}
				if (contains){
				for (int j = 0; j < blobs.length; j++) {
					if (blobs[j] == i) {
						if (solids[j] == true)
							break;
						offests[j] = 0;
					}
				}
				}
			}
		}

		int nNew = 0;

		int alpha = color(255, 0);
		offests = DepthAnalyser2D.getOffset(offests, nZ, nX, offset);
		for (int i = 0; i < offests.length; i++) {
			int x = getXInt(i);
			int y = getYInt(i);
			if (offests[i] < 10000 && offests[i] > 0) {
				if (x > x1 && x < x2 && y > y1 && y < y2) {
					imgOffset.pixels[i] = cyan;
				}
			}
		}

	}
	public void offsetthinLines(Box3D box,float offset,int[] coords){
		
		int[]candidates=new int[solids.length];
		int[]newCandidates=new int[solids.length];
		boolean[]blob=new boolean[solids.length];
		int nNew=0;
		for (int i=0;i<coords.length;i+=2){
			int cIndex=getIndex(coords[i],coords[i+1]);
			if (!solids[cIndex]){
				candidates[nNew]=cIndex;
				blob[cIndex]=true;
				nNew++;
			}
		}
		println("nNew: "+nNew);
		int x1=(int)box.x1;
		int x2=(int)box.x2;
		int y1=(int)box.z1;
		int y2=(int)box.z2;
		while (nNew>0){
			int nCandidates=nNew;
			nNew=0;
			for (int i=0;i<nCandidates;i++){
				int cand=candidates[i];
				
				int x= getXInt(cand);
				int y =getYInt(cand);
				
				if (x>x1&&x<x2&&y>y1&&y<y2){
					int leftI=cand-1;
				if (x>=x1&&!solids[leftI]&&!blob[leftI]){
					blob[leftI]=true;
					newCandidates[nNew]=leftI;
					nNew++;
				}
				int rightI=cand+1;
				x= (int) (rightI * 1f % nX);
				if (x<x2&&!solids[rightI]&&!blob[rightI]){
					blob[rightI]=true;
					newCandidates[nNew]=rightI;
					nNew++;
				}
				int topI=cand+nX;
				y = (int)(topI *1f/ nX);
				if (y>=y1&&!solids[topI]&&!blob[topI]){
					blob[topI]=true;
					newCandidates[nNew]=topI;
					nNew++;
				}
				int bottomI=cand-nX;
				y = (int)(bottomI *1f/ nX);
				if (y<y2&&!solids[bottomI]&&!blob[bottomI]){
					blob[bottomI]=true;
					newCandidates[nNew]=bottomI;
					nNew++;
				}
				}
			}
			for (int i=0;i<nNew;i++){
				
				candidates[i]=newCandidates[i];
			}
		}
		float[] offests=new float[blob.length];
		for (int i=0;i<blob.length;i++){
			if (blob[i]){
				offests[i]=0;
			}
			else{
				offests[i]=10000;
			}
		}
		
		
		int alpha=color(255,0);
		offests=DepthAnalyser2D.getOffset(offests,nZ,nX,offset);
		for (int i=0;i<offests.length;i++){
			int x=  getXInt(i);
			int y = getYInt(i);
			if (offests[i]<10000&&offests[i]>0){
				if (x>x1&&x<x2&&y>y1&&y<y2){
					imgOffset.pixels[i]=cyan;
				}
			}
		}
		
	}
	public void setupMenu() {
		Frame frame=findFrame();
		MenuListener myMenuListener=new MenuListener();
		MenuBar myMenu = new MenuBar();
		Menu topButton = new Menu("Box");
		MenuItem item1 = new MenuItem("New Box");
		topButton.add(item1);
		item1.addActionListener(myMenuListener);
		MenuItem item2 = new MenuItem("Delete Box");
		item2.addActionListener(myMenuListener);
		topButton.add(item2);
		myMenu.add(topButton);
		Menu topButton2 = new Menu("Display");
		myMenu.add(topButton2);
		MenuItem men02item1 = new MenuItem("Display Boxes");
		men02item1.addActionListener(myMenuListener);
		topButton2.add(men02item1);
		frame.setMenuBar(myMenu);
	}
	
	public Frame findFrame() {
		Container f = this.getParent();
		while (!(f instanceof Frame) && f != null)
			f = f.getParent();
		return (Frame) f;
	}
	
	public void setupGUI(){
		cam=new PCam2DGraphics(this);
		cam.setBounds(0-20, 0-20, nX+20, nZ+20);
		gui = new Gui(this, this.createDefaultFont(12));
		spinnerFrom=gui.addGuiSpinnerGroup(1100, 100, 56, 56, bdY2-1);
		spinnerTo=gui.addGuiSpinnerGroup(1170, 100, 123, 56, bdY2-1);
		gui.addGuiLabel(20, 80,  "Box");
		spX1=gui.addGuiSpinnerGroup(20, 100, 123, 56, 3055);
		spX2=gui.addGuiSpinnerGroup(100, 100, 123, 56, 3055);
		spY1=gui.addGuiSpinnerGroup(20, 120, 123, 56, bdY2-1);
		spY2=gui.addGuiSpinnerGroup(100, 120, 123, 56, bdY2-1);
		spZ1=gui.addGuiSpinnerGroup(20, 140, 123, 56, 3055);
		spZ2=gui.addGuiSpinnerGroup(100, 140, 123, 56, 3055);
	}
	public void setSelectedBox(Box3D b){
		spX1.setValue((int)b.x1);
		spY1.setValue((int)b.y1);
		spZ1.setValue((int)b.z1);
		spX2.setValue((int)b.x2);
		spY2.setValue((int)b.y2);
		spZ2.setValue((int)b.z2);
		currentBox=b;
	}
	public void loadBoxes(String path){
		fillBoxes=new ArrayList<Box3D>();
		String[]lines=this.loadStrings(path);
		for (int i=0;i<lines.length;i++){
			String[]ccords=lines[i].split(",");
			int[]cs=new int[ccords.length];
			for (int j=0;j<ccords.length;j++){
				cs[j]=Float.valueOf(ccords[j]).intValue();
			}
			fillBoxes.add(new Box3D(cs[0],cs[1],cs[2],cs[3],cs[4],cs[5]));
		}
		println("loaded");
	}
	public void saveBoxes(String path){
		String[]lines=new String[fillBoxes.size()];
		for (int i=0;i<fillBoxes.size();i++){
			Box3D b=fillBoxes.get(i);
			lines[i]=b.x1+","+b.y1+","+b.z1+","+b.x2+","+b.y2+","+b.z2;
		}
		this.saveStrings(path, lines);
		println("saved");
	}
	public int[][] blobs() {
		Arrays.fill(blobs,-1);
		int x1 = 0;
		int y1 = 0;
		int x2 = nX;
		int y2 = nZ;
		int iBlob = 0;
		for (int j = 0; j < solids.length; j++) {
			if (blobs[j] == -1) {
				blobs[j] = iBlob;
				boolean cCol = solids[j];
				Arrays.fill(candidates, 0);
				candidates[0]=j;
				Arrays.fill(newCandidates, 0);
				int nNew = 1;
				int size=0;
				while (nNew > 0) {
					int nCandidates = nNew;
					nNew = 0;
					for (int i = 0; i < nCandidates; i++) {
						int cand = candidates[i];
						int x = (int) (cand * 1f % nX);
						int y = (int) (cand * 1f / nX);
						int leftI = cand - 1;
						if (x > x1 && solids[leftI] == cCol
								&& blobs[leftI] == -1) {
							blobs[leftI] = iBlob;
							newCandidates[nNew] = leftI;
							nNew++;
						}
						int rightI = cand + 1;
						if (x < x2 - 1 && solids[rightI] == cCol
								&& blobs[rightI] == -1) {
							blobs[rightI] = iBlob;
							newCandidates[nNew] = rightI;
							nNew++;
						}
						int topI = cand + nX;
						if (y < y2 - 1 && solids[topI] == cCol
								&& blobs[topI] == -1) {
							blobs[topI] = iBlob;
							newCandidates[nNew] = topI;
							nNew++;
						}
						int bottomI = cand - nX;
						
						if (y > y1  && solids[bottomI] == cCol
								&& blobs[bottomI] == -1) {
							blobs[bottomI] = iBlob;
							newCandidates[nNew] = bottomI;
							nNew++;
						}
					}
					size+=nNew;
					for (int i=0;i<nNew;i++){
						candidates[i]=newCandidates[i];
					}
					
				}
				iBlob++;
			}
		}
		for (int x=0;x<nX;x++){
			for (int y=0;y<nZ;y++){
				if (x<bdX1||x>bdX2||y<bdZ1||y>bdZ2){
					int index=getIndex(x,y);
					blobs[index] = iBlob;
				}
			}
		}
		iBlob++;
		int[][]blobInfo=new int[iBlob][4];
		for (int i=0;i<blobInfo.length;i++){
			blobInfo[i][0]=1000000;
			blobInfo[i][1]=1000000;
			blobInfo[i][2]=-1000000;
			blobInfo[i][3]=-1000000;
		}
		for (int i=0;i<blobs.length;i++){
			int iBl=blobs[i];
			int x = (int) (i * 1f % nX);
			int y = (int) (i * 1f / nX);
			blobInfo[iBl][0]=min(blobInfo[iBl][0],x);
			blobInfo[iBl][1]=min(blobInfo[iBl][1],y);
			blobInfo[iBl][2]=max(blobInfo[iBl][2],x);
			blobInfo[iBl][3]=max(blobInfo[iBl][3],y);
		}
		println("iBlob: "+iBlob);
		return blobInfo;
	}
	//calculate blobs
	
	public void fill(int x,int y){
		int[]candidates=new int[solids.length];
		int[]newCandidates=new int[solids.length];
		int nNew=1;
		candidates[0]=getIndex(x,y);
		int setCol=color(255,255,0);
		boolean setSolid=false;
		if (imgNew.pixels[getIndex(x,y)]==setCol){
			setSolid=false;
			setCol=color(0);
			println("fill inverse");
		}
		
		imgNew.pixels[getIndex(x,y)]=setCol;
		
		while (nNew>0){
			int nCandidates=nNew;
			nNew=0;
			for (int i=0;i<nCandidates;i++){
				int cand=candidates[i];
				int leftI=cand-1;
				if (x>=0&&solids[leftI]==setSolid&&imgNew.pixels[leftI]!=setCol){
					imgNew.pixels[leftI]=setCol;
					newCandidates[nNew]=leftI;
					nNew++;
				}
				int rightI=cand+1;
				if (x<nX-1&&solids[rightI]==setSolid&&imgNew.pixels[rightI]!=setCol){
					imgNew.pixels[rightI]=setCol;
					newCandidates[nNew]=rightI;
					nNew++;
				}
				int topI=cand+nX;
				if (y>=0&&solids[topI]==setSolid&&imgNew.pixels[topI]!=setCol){
					imgNew.pixels[topI]=setCol;
					newCandidates[nNew]=topI;
					nNew++;
				}
				int bottomI=cand-nX;
				if (y<nZ-1&&solids[bottomI]==setSolid&&imgNew.pixels[bottomI]!=setCol){
					imgNew.pixels[bottomI]=setCol;
					newCandidates[nNew]=bottomI;
					nNew++;
				}
			}
			for (int i=0;i<nNew;i++){
				candidates[i]=newCandidates[i];
			}
		}
		
		
		imgNew.updatePixels();
	}
	public void writeToImage(boolean[] inside,PImage img){
		int white=color(255);
		int black=color(0,0);
		
		for (int i = 0; i < img.pixels.length; i++) {
			int cZ=i/nX;
			int cX=i%nX;
			
			if (inside[i]&&cX>bdX1&&cX<bdX2&&cZ>bdZ1&&cZ<bdZ2) {
				img.pixels[i]=white;
			}
			else{
				img.pixels[i]=black;
			}
		}
		img.updatePixels();
	}
	
	
	
	
	public void loadCuts(int y, boolean[] inside) {
		img=this.loadImage(getPath(y));
		for (int i = 0; i < img.pixels.length; i++) {
			int col = img.pixels[i];
			if (brightness(col) > 0) {
				inside[i] = true;
			}
			else{
				inside[i] = false;
			}
		}
		int colAlpha=color(255,0);
		for (int i = 0; i < imgNew.pixels.length; i++) {
			imgNew.pixels[i]=colAlpha;
		}
		imgNew.updatePixels();
	}
	
	public String getPath(int i){
		return pathCuts+"/solid"+(10000+i)+".png";
	}
	public String getPathNew(int i){
		return pathFixed+"/fixed"+(10000+i)+".png";
	}
	public String getPathOffset(int i){
		return pathOffset+"/offset"+(10000+i)+".png";
	}
	public void addBox(int x1,int y1,int z1,int x2,int y2,int z2){
		currentBox=new Box3D(x1,y1,z1,x2,y2,z2);
		fillBoxes.add(currentBox);
		setSelectedBox(currentBox);
	}
	
	public void loadBoxValues(Box3D b){
		b.setX1(spX1.getValue());
		b.setY1(spY1.getValue());
		b.setZ1(spZ1.getValue());
		b.setX2(spX2.getValue());
		b.setY2(spY2.getValue());
		b.setZ2(spZ2.getValue());
	}
	public void draw() {
		int newY=spinnerFrom.getValue();
		if (currentBox!=null){
			loadBoxValues(currentBox);
		}
		
		if (newY!=cY){
			cY=newY;
			loadCuts(cY,solids);
			writeToImage(solids,imgCanvas);
			
			imgOffset=this.loadImage(getPathOffset(cY));
			imgOffset.loadPixels();
			int iB=0;
			for (int i=0;i<imgOffset.pixels.length;i++){
				int col=imgOffset.pixels[i];
				if (brightness(col)>100)iB++;
				
			}
			println("iB: "+iB+"l: "+imgOffset.pixels.length);
			/*imgNew.save(getPathNew(cY));
			cY=newY;
			
			File file=new File(getPathNew(cY));
			if (file.exists()){
				imgNew=this.loadImage(getPathNew(cY));
			}
			else{
				imgNew=this.createImage(nX, nZ, ARGB);
			}
			*/
		}
		if (flagSaveBoxes){
			flagSaveBoxes=false;
			saveBoxes(pathBoxes);
		}
		if (flagAddBox){
			addBox(200,cY,200,nX-200,cY+1,nZ-200);
			println("box added");
			flagAddBox=false;
		}
		if (flagDeleteBox){
			flagDeleteBox=false;
			if (currentBox!=null){
				fillBoxes.remove(currentBox);
				currentBox=null;
			}
			
		}
		background(0);
		drawData();
		drawGui();
	}
	
	public void drawGui(){
		fill(10);
		stroke(100);
		rect(0,0,300,height);
		rect(1100,0,300,height);
		gui.display(g);
	}

	public void drawData() {
		g.pushMatrix();
		cam.useCam(g);
		if (frameCount == 1) {
			cam.zoomToBounds();
		}
		//

		int cSeg = 0;
		for (int i = 0; i < yCuts.length; i++) {
			if (yCuts[i] < cY)
				cSeg = i;
		}
		background(cSeg * 30, 0, 0);
		image(imgNew, 0, 0);
		image(imgOffset, 0, 0);
		image(imgCanvas, 0, 0);
		noFill();
		stroke(255, 0, 0);
		for (int i = 0; i < boundaries[0].length; i++) {
			Boundary bd = boundaries[0][i];
			rect(bd.x1, bd.y1, bd.x2 - bd.x1, bd.y2 - bd.y1);
		}
		if (displayBoxes) {
			stroke(0, 255, 255);
			for (int i = 0; i < allBoxes.size(); i++) {
				Box3D b = allBoxes.get(i);
				drawBox(b);
			}

			if (currentBox != null) {
				strokeWeight(8f);
				drawBox(currentBox);
			}
		}
		strokeWeight(1f);
		stroke(255, 0, 255);
		rect(xK(bdsFloat.x1), zK(bdsFloat.z1), (bdsFloat.x2 - bdsFloat.x1)/dim, (bdsFloat.z2 - bdsFloat.z1)/dim);
		stroke(0, 255, 255);
		// rect(offsetBox.x1,offsetBox.z1,offsetBox.x2-offsetBox.x1,offsetBox.z2-offsetBox.z1);
		stroke(0, 255, 0);

		for (int i = 0; i < xSegs.length; i++) {
			float x1 = xK(xSegs[i]);
			line(x1, bdZ1, x1, bdZ2);
		}
		for (int i = 0; i < zSegs.length; i++) {
			float z1 = zK(zSegs[i]);
			line(bdX1, z1, bdX2, z1);
		}
		g.popMatrix();
	}

	public void drawBox(Box3D b){
		if (cY>=b.getY1()&&cY<=b.getY2()){
			rect(b.x1,b.z1,b.x2-b.x1,b.z2-b.z1);
		}
	}
	boolean isBoxBorder(float x,float z,float border){
		if (x>xSegs[xSegs.length-1]-border)return true;
		
		if (z<zSegs[0]+border||z>zSegs[zSegs.length-1]-border)return true;
		return false;
	}
	public void subBox(float x1,float y1,float z1,float x2,float y2,float z2,float[][][]grid,DepthAnalyser3D dA,float sY,Boundary bounds){
		int ix1=this.xK(x1)/step;
		int ix2=this.xK(x2)/step;
		int iy1=((int)((y1-sY)/dim))/step;
		int iy2=((int)((y2-sY)/dim))/step;
		int iz1=this.zK(z1)/step;
		int iz2=this.zK(z2)/step;
		
		iz1=max(0,iz1);
		iz2=min(nZ/step,iz2);
		ix1=max(0,ix1);
		ix2=min(nX/step,ix2);
		iy1=max(0,iy1);
		iy2=min(grid[0].length,iy2);
		
		// todo differentiate it with the box borders...
		
		
		float offset = 0.02f / (dim * step);
		for (int x=ix1;x<ix2;x++){
			for (int y=iy1;y<iy2;y++){
				for (int z=iz1;z<iz2;z++){
					if (getX(x)>sX+0.02f)
					if (bounds.contains(x*step, z*step)){
					if (dA.depth[x][y][z]>offset){
						grid[x][y][z]=iso*2f;// outside
					}
					}
				}
			}
		}
	}
	public void addBox(float x1,float y1,float z1,float x2,float y2,float z2,float[][][]grid,DepthAnalyser3D dA,float sY,Boundary bounds){
		int ix1=this.xK(x1)/step;
		int ix2=this.xK(x2)/step;
		int iy1=((int)((y1-sY)/dim))/step;
		int iy2=((int)((y2-sY)/dim))/step;
		int iz1=this.zK(z1)/step;
		int iz2=this.zK(z2)/step;
		
		iz1=max(0,iz1);
		iz2=min(nZ/step,iz2);
		ix1=max(0,ix1);
		ix2=min(nX/step,ix2);
		iy1=max(0,iy1);
		iy2=min(grid[0].length,iy2);
		float offset = 0.02f / (dim * step);
		for (int x=ix1;x<ix2;x++){
			for (int y=iy1;y<iy2;y++){
				for (int z=iz1;z<iz2;z++){
					if (bounds.contains(x * step, z * step)) {
						if (dA.depth[x][y][z] > offset) {
							grid[x][y][z] = 0;// inside
						}
					}
				}
			}
		}
	}
	public void saveObjLevelFast(int y1, int y2, Boundary[] bounds, String path) {
		y1 -= 1;
		y2 += 1;
		vStream.startI = y1;
		vStream.initBuffer(y2 - y1);
		vStream.loadAllValues();
		println("YYYYY:" + ((int) ((y2 - y1) * 1f / step)));
		float[][][] grid = new float[(int) (nX * 1f / step)][(int) ((y2 - y1) * 1f / step)][(int) (nZ * 1f / step)];
		for (int y = 0; y < grid[0].length; y++) {
			imgOffset = this.loadImage(getPathOffset(y * step + y1));
			imgOffset.loadPixels();
			for (int x = 0; x < grid.length; x++) {
				for (int z = 0; z < grid[x][y].length; z++) {
					int col = imgOffset.pixels[getIndex(x * step, z * step)];
					// this.alp
					if (alpha(col) > 100) {
						if (vStream.getValue(x * step, (y * step + y1), z
								* step) < -iso) {
							vStream.setValue(x * step, (y * step + y1), z
									* step, 0);
						}
					}
				}
			}
		}

		println("gridY: " + grid[0].length);
		println("y2: " + y2);
		vStream.fillValueGrid(y1, y2, grid, step);
		float[][][] reached = new float[grid.length][grid[0].length][grid[0][0].length];
		float[][][] depth = new float[grid.length][grid[0].length][grid[0][0].length];
		DepthAnalyser3D dA = new DepthAnalyser3D();
		for (int x = 0; x < grid.length; x++) {
			for (int y = 0; y < grid[x].length; y++) {
				for (int z = 0; z < grid[x][y].length; z++) {
					if (grid[x][y][z] > -iso) {
						reached[x][y][z] = 1000;
					} else {
						reached[x][y][z] = 0;
					}
				}
			}
		}
		dA.calculateDepth(reached, depth);
		println("depth calculated");
		for (int i = 0; i < bounds.length; i++) {
			//for (int i = 2; i < 3; i++) {
			vStream.fillValueGrid(y1, y2, grid, step);
			Boundary bd = bounds[i];
			
			Box3D b3d = new Box3D(200, yCuts[4], 180, 940, yCuts[5], 560);
			Box3D b3dAdapt = new Box3D(b3d.x1 / step, 0, b3d.z1 / step, b3d.x2
					/ step, grid[0].length, b3d.z2 / step);
			// if (y1<=yCuts[4]&&y2>=)
			// this.mirrorXGrid(grid, b3dAdapt,bdX2/step-3);
			for (int x = 0; x < grid.length; x++) {
				for (int y = 0; y < grid[x].length; y++) {
					for (int z = 0; z < grid[x][y].length; z++) {
						float c = grid[x][y][z];
						int cX=x*step;
						int cY=y*step;
						int inside = bd.isBorder(x * step, z * step);
						if (inside != Boundary.OUTSIDE) {
							// inside border
							if ((inside == Boundary.BORDER || y == 1
									|| y == grid[x].length - 2) && (z!=boxZ[6]||i!=4)) {
								// boxZ[6]==mirrorAxis || box 2 exception...
								if (c < 0) c = c * -1f;// reached
								else if (c > 0) c = 0;// not reached
								//else if (c > iso) c = 0;// not reached
								
							} else {
								// inside volume
								if (c < 0) c = c * -1f;// reached	
								else if (c > iso)c = 0;// not reached
							}
						} else {
							c = iso * 2f;// reached
						}
						grid[x][y][z] = c;
					}
				}
			}

			
			float offset = 0.04f / (dim * step);
			println("offset: " + offset);

			for (int x = 0; x < grid.length; x++) {
				for (int y = 0; y < grid[x].length; y++) {
					for (int z = 0; z < grid[x][y].length; z++) {
						if (dA.depth[x][y][z] > offset) {
							// if not ingrid
							grid[x][y][z] = iso * 2f;// outside
						}
					}
				}
			}

			//Boundaries
			float by1 = getY(y1);
			float by2 = getY(y2);
			float bx1 = getX((int) bd.x1);
			float bx2 = getX((int) bd.x2);
			float bz1 = getZ((int) bd.y1);
			float bz2 = getZ((int) bd.y2);
			this.addBox(bx1, by1, bz1, bx1+0.04f, by2, bz2, grid, dA, by1, bd);
			this.addBox(bx2-0.04f, by1, bz1, bx2, by2, bz2, grid, dA, by1, bd);
			this.addBox(bx1, by1, bz1, bx2, by2, bz1+0.04f, grid, dA, by1, bd);
			this.addBox(bx1, by1, bz2-0.04f, bx2, by2, bz2, grid, dA,by1, bd);
			
			// segments
			for (int ii = 1; ii < xSegs.length - 1; ii++) {
				float x1 = xSegs[ii] - 0.02f;
				float x2 = xSegs[ii] + 0.02f;
				this.addBox(x1, by1, bz1, x2,by2, bz2, grid, dA, by1,bd);
			}
			for (int ii = 1; ii < zSegs.length - 1; ii++) {
				float z1 = zSegs[ii] - 0.02f;
				float z2 = zSegs[ii] + 0.02f;
				this.addBox(bx1, by1, z1, bx2,by2, z2, grid, dA, by1,bd);
			}
			
			// vertical stick
			
			float dHole=0.032f;
			float dBord=0.018f;
			float xF1=xSegs[5]-0.04f-dBord*2-dHole;
			float xF2=xSegs[5];
			float zF1=zSegs[2]-0.02f-dBord*2-dHole;
			float zF2=zF1+0.02f+dBord*2+dHole;
			float y1f=by1+0.04f;
			float y2f=by2-0.04f;
			this.addBox(xF1, y1f, zF1, xF2,y2f, zF2, grid, dA, by1,bd);
			this.subBox(xF1+dBord, y1f, zF1+dBord,xF1+dBord+dHole, y2f, zF1+dBord+dHole, grid, dA, by1,bd);
			zF1=zSegs[3];
			 zF2=zF1+0.02f+dBord*2+dHole;
			this.addBox(xF1, y1f, zF1, xF2,y2f, zF2, grid, dA, by1,bd);
			this.subBox(xF1+dBord, y1f, zF2-dBord-dHole,xF1+dBord+dHole, y2f, zF2-dBord, grid, dA, by1,bd);
		
			//propfen
			float dPropf=0.07f;
			y1f=by2-0.05f;
			y2f=by2;
			float y3f=by1;
			float y4f=by1+0.05f;
			for (int x=1;x<xSegs.length-1;x++){
				for (int z=1;z<zSegs.length-1;z++){
					 xF1=xSegs[x];
					 zF1=zSegs[z]-dPropf;
					 this.addBox(xF1,y1f, zF1, xF1+dPropf, y2f, zF1+dPropf, grid, dA, by1,bd);
					 this.addBox(xF1,y3f, zF1, xF1+dPropf, y4f, zF1+dPropf, grid, dA, by1,bd);
				}
			}
			
			float cY1=by1+0.15f;
			float cY2=by1+0.19f;// 40mm higher
			float off=dHole*0.5f;
			
			// holes
			for (int ii = 0; ii < xSegs.length -1; ii++) {
				float mid=(xSegs[ii]+xSegs[ii+1])*0.5f;
				this.subBox(mid-off, cY1-off, sZ, mid+off, cY1+off, eZ, grid, dA, by1,bd);
			}
			for (int ii = 0; ii < zSegs.length -1; ii++) {
				float mid=(zSegs[ii]+zSegs[ii+1])*0.5f;
				this.subBox(sX, cY2-off, mid-off, eX, cY2+off, mid+off, grid, dA, by1,bd);
			}
			// top and down
			for (int x = 0; x < grid.length; x++) {
				for (int z = 0; z < grid[0][0].length; z++) {
					grid[x][0][z] = iso * 2f;// outsid
					grid[x][grid[x].length - 1][z] = iso * 2f;// outsid
				}
			}
			
			
			CAADMCube mCube = new CAADMCube();
			String name = path + "_" + i + "_" + "step_" + step + ".obj";
			// mirroring
			if (i==4){
				int x1=(int)bd.x1-1;
				int x2=(int)bd.x2+1;
				int z1=(int)bd.y1-1;
				int z2=(int)bd.y2; // one not enough
				//0,1,2,3,4
				//0,1,2,3,3,2,1,0
				float[][][]smallGrid= new float[x2-x1][grid[0].length][(z2-z1)*2];
				for (int x=x1;x<x2;x++){
					for (int y=0;y<grid[0].length;y++){
						for (int z=z1;z<=z2;z++){
							smallGrid[x-x1][y][z-z1]=grid[x][y][z];
						}
					}
				}
				
				for (int x=0;x<smallGrid.length;x++){
					for (int y=0;y<smallGrid[0].length;y++){
						for (int z=0;z<=(z2-z1);z++){
							smallGrid[x][y][smallGrid[0][0].length-z-1]=smallGrid[x][y][z];
						}
					}
				}
				mCube.marchingCubesMinim(smallGrid, iso, dim * step * 1000f, getX(x1) * 1000f,
						getY(y1) * 1000f, getZ(z1) * 1000f, name);
			}
			else{
				mCube.marchingCubesMinim(grid, iso, dim * step * 1000f,  sX * 1000f,
						getY(y1) * 1000f, sZ * 1000f, name);
			}
			println("MC Grid created");
		}
	}
	public void saveObjLevel(int y1, int y2, Boundary[] bounds, String path) {
		y1 -= 1;
		y2 += 1;
		vStream.startI = y1;
		vStream.initBuffer(y2 - y1);
		vStream.loadAllValues();
		println("YYYYY:" + ((int) ((y2 - y1) * 1f / step)));
		float[][][] grid = new float[(int) (nX * 1f / step)][(int) ((y2 - y1) * 1f / step)][(int) (nZ * 1f / step)];
		for (int y = 0; y < grid[0].length; y++) {
			imgOffset = this.loadImage(getPathOffset(y * step + y1));
			imgOffset.loadPixels();
			for (int x = 0; x < grid.length; x++) {
				for (int z = 0; z < grid[x][y].length; z++) {
					int col = imgOffset.pixels[getIndex(x * step, z * step)];
					// this.alp
					if (alpha(col) > 100) {
						if (vStream.getValue(x * step, (y * step + y1), z
								* step) < -iso) {
							vStream.setValue(x * step, (y * step + y1), z
									* step, 0);
						}
					}
				}
			}
		}

		println("gridY: " + grid[0].length);
		println("y2: " + y2);
		float[][][] reached = new float[grid.length][grid[0].length][grid[0][0].length];
		float[][][] depth = new float[grid.length][grid[0].length][grid[0][0].length];
		DepthAnalyser3D dA = new DepthAnalyser3D();
		for (int i = 0; i < bounds.length; i++) {
			//for (int i = 4; i < 5; i++) {
			Boundary bd = bounds[i];
			vStream.fillValueGrid(y1, y2, grid, step);
			Box3D b3d = new Box3D(200, yCuts[4], 180, 940, yCuts[5], 560);
			Box3D b3dAdapt = new Box3D(b3d.x1 / step, 0, b3d.z1 / step, b3d.x2
					/ step, grid[0].length, b3d.z2 / step);
			// if (y1<=yCuts[4]&&y2>=)
			// this.mirrorXGrid(grid, b3dAdapt,bdX2/step-3);
			for (int x = 0; x < grid.length; x++) {
				for (int y = 0; y < grid[x].length; y++) {
					for (int z = 0; z < grid[x][y].length; z++) {
						float c = grid[x][y][z];
						int inside = bd.isBorder(x * step, z * step);
						if (inside != Boundary.OUTSIDE) {
							// inside border
							if (inside == Boundary.BORDER || y == 1
									|| y == grid[x].length - 2) {
								if (c > 0) {
									c = 0;// not reached
								}
								if (c < 0) {
									c = c * -1f;// reached
								}
							} else {
								// inside volume
								if (c < 0) {
									// reached
									c = c * -1f;
								} else if (c > iso) {
									// not reached
									c = 0;
								}
							}
						} else {
							c = iso * 2f;// reached
						}
						grid[x][y][z] = c;
					}
				}
			}

			for (int x = 0; x < grid.length; x++) {
				for (int y = 0; y < grid[x].length; y++) {
					for (int z = 0; z < grid[x][y].length; z++) {
						if (grid[x][y][z] > iso) {
							reached[x][y][z] = 0;
						} else {
							reached[x][y][z] = 1000;
						}
					}
				}
			}

			// make everthing that is not reached
			// massiv
			// inner holes

			dA.calculateDepth(reached, depth);
			float offset = 0.04f / (dim * step);
			println("offset: " + offset);

			for (int x = 0; x < grid.length; x++) {
				for (int y = 0; y < grid[x].length; y++) {
					for (int z = 0; z < grid[x][y].length; z++) {
						if (dA.depth[x][y][z] > offset) {
							// if not ingrid
							grid[x][y][z] = iso * 2f;// outside
						}
					}
				}
			}

			// segments
			for (int ii = 1; ii < xSegs.length - 1; ii++) {
				int x1 = xK(xSegs[ii] - 0.02f) / step;
				int x2 = xK(xSegs[ii] + 0.02f) / step;
				println("x1: " + x1 + "x2: " + x2);
				for (int x = x1; x < x2; x++) {
					for (int y = 0; y < grid[x].length; y++) {
						for (int z = 0; z < grid[x][y].length; z++) {
							if (dA.depth[x][y][z] > offset) {
								grid[x][y][z] = 0;

							}
						}
					}
				}
			}
			
			for (int ii = 1; ii < zSegs.length - 1; ii++) {
				int z1 = zK(zSegs[ii] - 0.02f) / step;
				int z2 = zK(zSegs[ii] + 0.02f) / step;
				println("z1: " + z1 + "z2: " + z2);
				for (int z = z1; z < z2; z++) {
					for (int y = 0; y < grid[0].length; y++) {
						for (int x = 0; x < grid.length; x++) {
							if (dA.depth[x][y][z] > offset) {
								grid[x][y][z] = 0;
							}
						}
					}
				}
			}
			// vertical stick
			println("getY(y1): "+getY(y1));
			println("yK: "+yK(getY(y1)));
			float dOff=0.05f;
			float dHole=0.032f;
			float dBord=0.02f;
			float dStick=dOff+dHole+dBord;// offset wand + 32mm+20mm
			float xF=xSegs[5]-dStick;
			float zF=zSegs[2]-dStick;
			float y1f=getY(y1)+0.04f;
			float y2f=getY(y2)-0.04f;
			this.addBox(xF, y1f, zF, xF+dStick,y2f, zF+dStick, grid, dA, getY(y1),bd);
			this.subBox(xF+dBord, y1f, zF+dStick-dBord-dHole,xF+dBord+dHole, y2f, zF+dStick-dBord, grid, dA, getY(y1),bd);
			 xF=xSegs[5]-dStick;
			 zF=zSegs[4]-dStick;
			this.addBox(xF,y1f, zF, xF+dStick, y2f, zF+dStick, grid, dA, getY(y1),bd);
			this.subBox(xF+dBord, y1f, zF+dStick-dBord-dHole,xF+dBord+dHole, y2f, zF+dStick-dBord, grid, dA, getY(y1),bd);
		
			//propfen
			float dPropf=0.07f;
			y1f=getY(y2)-0.05f;
			y2f=getY(y2);
			float y3f=getY(y1);
			float y4f=getY(y1)+0.05f;
			for (int x=1;x<xSegs.length-1;x++){
				for (int z=1;z<zSegs.length-1;z++){
					 xF=xSegs[x];
					 zF=zSegs[z]-dPropf;
					 this.addBox(xF,y1f, zF, xF+dPropf, y2f, zF+dPropf, grid, dA, getY(y1),bd);
					 this.addBox(xF,y3f, zF, xF+dPropf, y4f, zF+dPropf, grid, dA, getY(y1),bd);
				}
			}
			
			
			float cY1=getY(y1)+0.15f;
			float cY2=getY(y1)+0.19f;// 40mm higher
			float off=dHole*0.5f;
			
			// holes
			for (int ii = 0; ii < xSegs.length -1; ii++) {
				float mid=(xSegs[ii]+xSegs[ii+1])*0.5f;
				this.subBox(mid-off, cY1-off, sZ, mid+off, cY1+off, eZ, grid, dA, getY(y1),bd);
			}
			for (int ii = 0; ii < zSegs.length -1; ii++) {
				float mid=(zSegs[ii]+zSegs[ii+1])*0.5f;
				this.subBox(sX, cY2-off, mid-off, eX, cY2+off, mid+off, grid, dA, getY(y1),bd);
			}
			// top and down
			for (int x = 0; x < grid.length; x++) {
				for (int z = 0; z < grid[0][0].length; z++) {
					grid[x][0][z] = iso * 2f;// outsid
					grid[x][grid[x].length - 1][z] = iso * 2f;// outsid
				}
			}
			
			
			
			println("sZ: " + sZ);
			CAADMCube mCube = new CAADMCube();
			String name = path + "_" + i + "_" + "step_" + step + ".obj";
			mCube.marchingCubesMinim(grid, iso, dim * step * 1000f, sX * 1000f,
					getY(y1) * 1000f, sZ * 1000f, name);
			println("MC Grid created");
		}
	}
	public void saveObjLevel(int y1, int y2, Boundary[] bounds) {
		y1 -= 1;
		y2 += 1;
		vStream.startI = y1;
		vStream.initBuffer(y2 - y1);
		vStream.loadAllValues();
		float[][][] grid = new float[nX][y2 - y1][nZ];
		for (int y = 0; y < grid[0].length; y++) {
			imgOffset = this.loadImage(getPathOffset(y + y1));
			imgOffset.loadPixels();
			for (int x = 0; x < grid.length; x++) {
				for (int z = 0; z < grid[x][y].length; z++) {
					int col = imgOffset.pixels[getIndex(x, z)];
					//this.alp
					if (alpha(col) > 100) {
						if (vStream.getValue(x, y + y1, z) < -iso) {
							
							vStream.setValue(x, y + y1, z, 0);
						}

					}
				}
			}
		}
		
		println("gridY: " + grid[0].length);
		println("y2: " + y2);
		float[][][] reached=new float[grid.length][grid[0].length][grid[0][0].length];
		float[][][] depth=new float[grid.length][grid[0].length][grid[0][0].length];
		DepthAnalyser3D dA=new DepthAnalyser3D();
		for (int i = 0; i < bounds.length; i++) {
			Boundary bd = bounds[i];
			vStream.fillValueGrid(y1, y2, grid);
			for (int x = 0; x < grid.length; x++) {
				for (int y = 0; y < grid[x].length; y++) {
					for (int z = 0; z < grid[x][y].length; z++) {
						float c = grid[x][y][z];
						int inside = bd.isBorder(x, z);
						if (inside != Boundary.OUTSIDE ) {
							// inside border
							if (inside == Boundary.BORDER || y == 1
									|| y == grid[x].length - 2) {
								if (c > 0) {
									c = 0;// not reached
								}
								if (c < 0) {
									c = c * -1f;// reached
								}
							} else {
								// inside volume
								if (c < 0) {
									// reached
									c = c * -1f;
								} else if (c > iso) {
									// not reached
									c = 0;
								}
							}
						} else {
							c = iso * 2f;// reached
						}
						grid[x][y][z] = c;
					}
				}
			}
			
			for (int x=0;x<grid.length;x++){
				for (int y=0;y<grid[x].length;y++){
					for (int z=0;z<grid[x][y].length;z++){
						if (grid[x][y][z]>iso){
							reached[x][y][z]=0;
						}
						else{
							reached[x][y][z]=1000;
						}
					}
				}
			}
			
			// make everthing that is not reached 
			//massiv
			//inner holes		
			
			dA.calculateDepth(reached,depth);
			float offset=0.04f/dim;
			println("offset: "+offset);
			
			for (int x=0;x<grid.length;x++){
				for (int y=0;y<grid[x].length;y++){
					for (int z=0;z<grid[x][y].length;z++){
						
						 if (dA.depth[x][y][z]>offset){
							//if not ingrid
							grid[x][y][z]=iso*2f;// outside
						}
					}
				}
			}
		for (int ii=0;ii<xSegs.length;ii++){
			int x1=xK(xSegs[ii]-0.02f);
			int x2=xK(xSegs[ii]+0.02f);
			println("x1: "+x1+"x2: "+x2);
			for (int x=x1;x<x2;x++){
				for (int y=0;y<grid[x].length;y++){
					for (int z=0;z<grid[x][y].length;z++){
						if (dA.depth[x][y][z]>offset){
							grid[x][y][z]=0;
							
						}
					}
				}
			}
		}
		for (int ii=0;ii<zSegs.length;ii++){
			int z1=zK(zSegs[ii]-0.02f);
			int z2=zK(zSegs[ii]+0.02f);
			println("z1: "+z1+"z2: "+z2);
			for (int z=z1;z<z2;z++){
				for (int y=0;y<grid[0].length;y++){
					for (int x=0;x<grid.length;x++){
						if (dA.depth[x][y][z]>offset){
							grid[x][y][z]=0;
							
						}
					}
				}
			}
		}
		for (int x=0;x<grid.length;x++){
				for (int z=0;z<grid[0][0].length;z++){
		
			grid[x][0][z]=iso*2f;// outsid
			grid[x][grid[x].length-1][z]=iso*2f;// outsid
		}
			}
			println("sZ: " + sZ);
			CAADMCube mCube = new CAADMCube();
			String name = path + "test" + bd.x1 + "_" + y1 + "_" + bd.y1 + "_"
					+ bd.x2 + "_" + y2 + "_" + bd.y2 + ".obj";
			mCube.marchingCubesMinim(grid, iso, dim * 1000f, sX * 1000f,
					getY(y1) * 1000f, sZ * 1000f, name);
			println("MC Grid created");
		}
	}
	public void saveObjLevelOld(int y1, int y2, Boundary[] bounds) {
		y1 -= 1;
		y2 += 1;
		vStream.startI = y1;
		vStream.initBuffer(y2 - y1);
		vStream.loadAllValues();
		float[][][] grid = new float[nX][y2 - y1][nZ];
		for (int y = 0; y < grid[0].length; y++) {
			imgOffset = this.loadImage(getPathOffset(y + y1));
			imgOffset.loadPixels();
			for (int x = 0; x < grid.length; x++) {
				for (int z = 0; z < grid[x][y].length; z++) {
					int col = imgOffset.pixels[getIndex(x, z)];
					//this.alp
					if (alpha(col) > 100) {
						if (vStream.getValue(x, y + y1, z) < -iso) {
							
							vStream.setValue(x, y + y1, z, 0);
						}

					}
				}
			}
		}
		
		println("gridY: " + grid[0].length);
		println("y2: " + y2);
		for (int i = 0; i < bounds.length; i++) {
			Boundary bd = bounds[i];
			vStream.fillValueGrid(y1, y2, grid);
			for (int x = 0; x < grid.length; x++) {
				for (int y = 0; y < grid[x].length; y++) {
					for (int z = 0; z < grid[x][y].length; z++) {
						float c = grid[x][y][z];
						int inside = bd.isBorder(x, z);
						if (inside != Boundary.OUTSIDE && y > 0
								&& y < grid[x].length - 1) {
							// inside border
							if (inside == Boundary.BORDER || y == 1
									|| y == grid[x].length - 2) {
								if (c > 0) {
									c = 0;// not reached
								}
								if (c < 0) {
									c = c * -1f;// reached
								}
							} else {
								// inside volume
								if (c < 0) {
									// reached
									c = c * -1f;
								} else if (c > iso) {
									// not reached
									c = 0;
								}
							}
						} else {
							c = iso * 2f;// reached
						}
						grid[x][y][z] = c;
					}
				}
			}
			float[][][] reached=new float[grid.length][grid[0].length][grid[0][0].length];
			for (int x=0;x<grid.length;x++){
				for (int y=0;y<grid[x].length;y++){
					for (int z=0;z<grid[x][y].length;z++){
						if (grid[x][y][z]>iso){
							reached[x][y][z]=0;
						}
						else{
							reached[x][y][z]=1000;
						}
					}
				}
			}
			
			// make everthing that is not reached 
			//massiv
			//inner holes		
			DepthAnalyser3D dA=new DepthAnalyser3D();
			dA.calculateDepth(reached);
			float offset=0.02f/dim;
			println("offset: "+offset);
			
			for (int x=0;x<grid.length;x++){
				for (int y=0;y<grid[x].length;y++){
					for (int z=0;z<grid[x][y].length;z++){
						if (dA.depth[x][y][z]>offset){
							grid[x][y][z]=iso*2f;// outside
						}
					}
				}
			}
			println("sZ: " + sZ);
			CAADMCube mCube = new CAADMCube();
			String name = path + "test" + bd.x1 + "_" + y1 + "_" + bd.y1 + "_"
					+ bd.x2 + "_" + y2 + "_" + bd.y2 + ".obj";
			mCube.marchingCubesMinim(grid, iso, dim * 1000f, sX * 1000f,
					getY(y1) * 1000f, sZ * 1000f, name);
			println("MC Grid created");
		}
	}
	public void selectClosestBox(){
		Box3D closestBox=null;
		float closestD=Float.MAX_VALUE;
		for (int i=0;i<fillBoxes.size();i++){
			Box3D b=fillBoxes.get(i);
			
			if (b.contains(cam.getModelX(mouseX), cY, cam.getModelY(mouseY))){
				float dX=cam.getModelX(mouseX)-b.getCenterX();
				float dY=cam.getModelY(mouseY)-b.getCenterZ();
				float d=dX*dX+dY*dY;
				if (d<closestD){
					closestBox=b;
					closestD=d;
				}
			}
		}
		if (closestBox!=null){
			setSelectedBox(closestBox);
		}
		else {
			currentBox=null;
		}
	}
	public int getRoundDown(float value,float dim){
		return (int)(value/dim);
	}
	public int getRoundUp(float value,float dim){
		return (int)(value/dim)+1;
	}
	public int getXInt(int i){
		return  (int) (i * 1f % nX);
	}

	public int getYInt(int i){
		return  (int) (i * 1f / nX);
	}

	int getIndex(int x,int y){
		return y*nX+x;
	}
	public int xK(float x) {
		return round ((x - sX) / dim);
	}

	public int yK(float y) {
		return round ((y - sY) / dim);
	}

	public int zK(float z) {
		return round ((z - sZ) / dim);
	}
	public float getX(int x) {
		return x * dim + sX;
	}

	
	public float getY(int y) {
		return y * dim + sY;
	}

	public float getZ(int z) {
		return z * dim + sZ;
	}
	public void mousePressed(){
		int cX=(int)cam.getModelX(mouseX);
		int cY=(int)cam.getModelY(mouseY);
		if (cX>0&&cX<nX&&cY>0&&cY<nZ){
			//fill(cX,cY);
			//offsetthinLines(offsetBox,20,cX,cY);
		}
		if (mouseX>300&&mouseX<width-300){
		selectClosestBox();
		}
		println("filled");
	}

	public void keyPressed(){
		if (key=='s')flagSaveBoxes=true;
	}
	class Boundary extends Rect2D{
		final static int INSIDE=0;
		final static int BORDER=1;
		final static int OUTSIDE=2;
		
		public Boundary(float x1, float y1, float x2, float y2) {
			super(x1, y1, x2, y2);
			// TODO Auto-generated constructor stub
		}

		int isBorder(int x,int y){
			if (x>=x1&&x<x2&&y>=y1&&y<y2){
				if (x==x1||x==x2-1||y==y1||y==y2-1)return BORDER;
				return INSIDE;
			}
			return OUTSIDE;
		}
	}
	class MenuListener implements ActionListener { 
		MenuListener() {
		  }

		@Override
		public void actionPerformed(ActionEvent e) {
			// TODO Auto-generated method stub
			if (((MenuItem)e.getSource()).getLabel().equals("New Box")){
				flagAddBox=true;
			}
			if (((MenuItem)e.getSource()).getLabel().equals("Delete Box")){
				flagDeleteBox=true;
			}
			if (((MenuItem)e.getSource()).getLabel().equals("Display Boxes")){
				displayBoxes=!displayBoxes;
			}
		} 
	}
}
